package com.wiz.web.service.impl;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.google.gson.reflect.TypeToken;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.wiz.common.TencentSignature;
import com.wiz.vo.criteria.PayCriteria;
import com.wiz.vo.to.AccountChargeHistoryTO;
import com.wiz.vo.to.PayOrderTO;
import com.wiz.vo.to.tencent.WxPayReqData;
import com.wiz.vo.to.tencent.WxPayResData;
import com.wiz.vo.to.tencent.WxReturnData;
import com.wiz.vo.type.PayMethodType;
import com.wiz.web.dao.mapper.WxPayMapper;
import com.wiz.web.manager.HttpManager;
import com.wiz.web.manager.HttpSSLManager;
import com.wiz.web.manager.fatory.WxDataFactory;
import com.wiz.web.service.WxService;

@SuppressWarnings("all")
@Service("wxService")
public class WxServiceImpl extends BasicServiceImpl implements WxService {
	private transient final Logger log = LoggerFactory.getLogger(getClass());

	@Resource
	private WxDataFactory wxDataFactory;
	@Resource
	private WxPayMapper wxPayMapper;
	@Resource
	private HttpManager httpManager;
	@Resource
	private HttpSSLManager httpSSLManager;
	
	@Override
	public String wxBarPay(String msg, String hqId, String stationId,String wxMerchantId) throws Exception{
		log.info("stationId="+stationId+" using wxBarPay,params="+msg);
		PayCriteria criteria = gson.fromJson(msg, new TypeToken<PayCriteria>(){}.getType());
		criteria.setHqId(hqId);
		criteria.setStationId(stationId);
		criteria.setFuellingOrderId(criteria.getOutTradeNo());
		criteria.setOutTradeNo(createWxOutTradeNo(stationId));
		criteria.setSubMchId(wxMerchantId);
		WxPayReqData reqData = (WxPayReqData) wxDataFactory.getDataByType(criteria,PAY_KEY,PayMethodType.BAR_PAY.getId());
		String response = httpManager.sendWxPost(WX_BAR_PAY, reqData);
		WxPayResData resData = handlerWxXmlToResData(response);
		if(resData.getReturn_code().equals(WX_RETURN_CODE_SUCCESS)){//通讯成功
			if(resData.getResult_code().equals(WX_RETURN_CODE_SUCCESS)){//支付成功
				criteria.setTransactionId(resData.getTransaction_id());
				criteria.setResult(response);
				criteria.setPayTime(resData.getTime_end());
				criteria.setStatus(1);
				wxPayMapper.insertWxPayOrder(criteria);
				PayOrderTO order = new PayOrderTO();
				order.setFuellingOrderId(criteria.getFuellingOrderId());
				order.setPayOrderId(resData.getOut_trade_no());
				order.setRefOrderId(resData.getTransaction_id());
				return returnResult(CODE_OK, STRING_OK, order);
			}else{
				if(resData.getErr_code() != null && resData.getErr_code().equals(WX_RETURN_CODE_USERPAYING)){//待密码
					int each = 0;
					WxPayResData responseData = null;
					Thread.currentThread().sleep(2000);
					while(each<4){//30秒后
						criteria.setTransactionId(resData.getTransaction_id());
						responseData = queryWxOrder(criteria,PAY_KEY);
						if(responseData.getReturn_code().equals(WX_RETURN_CODE_SUCCESS) && 
								responseData.getResult_code().equals(WX_RETURN_CODE_SUCCESS)){
							if(responseData.getTrade_state() != null && responseData.getTrade_state().equals(WX_RETURN_CODE_SUCCESS)){
								criteria.setResult(response);
								criteria.setPayTime(resData.getTime_end());
								criteria.setStatus(1);
								wxPayMapper.insertWxPayOrder(criteria);
								PayOrderTO order = new PayOrderTO();
								order.setFuellingOrderId(criteria.getFuellingOrderId());
								order.setPayOrderId(resData.getOut_trade_no());
								order.setRefOrderId(resData.getTransaction_id());
								return returnResult(CODE_OK, STRING_OK, order);
							}
							if(responseData.getTrade_state() != null && 
									!responseData.getTrade_state().equals(WX_RETURN_CODE_USERPAYING)){//非用户支付中 状态下，直接返回结果
								return returnResult(CODE_WX_ERROR, responseData.getTrade_state_desc(), null);
							}
						}
						if(each==3){
							log.info(criteria.getOutTradeNo()+" will Reverse order");
							return getResponseFromReverse(criteria);
						}
						each++;
						Thread.currentThread().sleep(10000);
					}
				}
			}
		}else{
			return RESPONSE_WX_NO_RESPONSE;
		}
		log.warn("stationId="+stationId+"pay fail,response="+resData.getErr_code_des()+" || "+resData.getTrade_state_desc()
				+",reverseWxOrder will begin after 16s");
		Thread.currentThread().sleep(16000);
		criteria.setTransactionId(resData.getTransaction_id());
		return getResponseFromReverse(criteria);
	}

	/**
	 * 查询微信订单
	 * @param criteria
	 * @return
	 */
	private WxPayResData queryWxOrder(PayCriteria criteria,String key){
		WxPayReqData payData = (WxPayReqData) wxDataFactory.getDataByType(criteria,key,PayMethodType.QUERY.getId());
		return handlerWxXmlToResData(httpManager.sendWxPost(WX_BAR_ORDER_SEARCH, payData));
	}
	
	/**
	 * 撤销微信订单
	 * @param criteria
	 * @return
	 */
	private WxPayResData reverseWxOrder(PayCriteria criteria,String key){
		WxPayReqData payData = (WxPayReqData) wxDataFactory.getDataByType(criteria,key,PayMethodType.REVERSE.getId());
		return handlerWxXmlToResData(httpSSLManager.sendWxPost(WX_BAR_REVERSE, payData));
	}
	
	/**
	 * 解析撤销返回结果
	 * @param criteria
	 * @return
	 */
	private String getResponseFromReverse(PayCriteria criteria){
		WxPayResData responseDataReverse = reverseWxOrder(criteria,PAY_KEY);
		log.info("WX responseDataReverse="+responseDataReverse.toString());
		if(responseDataReverse.getReturn_code().equals(WX_RETURN_CODE_SUCCESS)){
			if(responseDataReverse.getResult_code().equals(WX_RETURN_CODE_SUCCESS)){//撤销成功
				return RESPONSE_WX_PAY_FAIL;
			}else {//Result_code=fail
				if(responseDataReverse.getErr_code().equals(WX_RETURN_CODE_SYSTEMERROR)){//请立即调用被扫订单结果查询API，查询当前订单状态，并根据订单的状态决定下一步的操作。
					WxPayResData responseData = queryWxOrder(criteria,PAY_KEY);
					if(responseData.getTrade_state() != null && 
							responseData.getTrade_state().equals(WX_RETURN_CODE_SUCCESS)){//订单已经支付成功，无法取消
						criteria.setTransactionId(responseData.getTransaction_id());
						criteria.setResult(gson.toJson(responseData));
						criteria.setPayTime(responseData.getTime_end());
						criteria.setStatus(1);
						wxPayMapper.insertWxPayOrder(criteria);
						PayOrderTO order = new PayOrderTO();
						order.setFuellingOrderId(criteria.getFuellingOrderId());
						order.setPayOrderId(responseData.getOut_trade_no());
						order.setRefOrderId(responseData.getTransaction_id());
						return returnResult(CODE_OK, STRING_OK, order);
					}else{
//						if(responseData.getTrade_state() != null && 
//								responseData.getTrade_state().equals(WX_RETURN_CODE_USERPAYING)){
//							reverseWxOrder(criteria,PAY_KEY);
//						}
//						if(responseData.getTrade_state() != null && 
//								responseData.getTrade_state().equals(WX_RETURN_CODE_NOTPAY)){
//							reverseWxOrder(criteria,PAY_KEY);
//						}
						log.warn("Reverse wxOrder fail, result="+responseDataReverse.toString());
						return RESPONSE_WX_PAY_FAIL;
					}
				}
			}
		}else{
			log.warn("Reverse wxOrder fail, result="+responseDataReverse.toString());
//			return returnResult(CODE_WX_ERROR, responseDataReverse.getErr_code_des(), null);
		}
		return RESPONSE_WX_PAY_FAIL;
	}
	
	/**
	 * 微信返回组装成实体类
	 * @param response
	 * @return
	 */
	private WxPayResData handlerWxXmlToResData(String response){
		XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8",
				new XmlFriendlyNameCoder("-_", "_")));
		response = response.replace("<xml>", "<com.wiz.vo.to.tencent.WxPayResData>");
		response = response.replace("</xml>", "</com.wiz.vo.to.tencent.WxPayResData>");
		return (WxPayResData) xStreamForRequestPostData.fromXML(response);
	}
	
	/**
	 * 支付订单号
	 * @param stationId
	 * @return
	 */
	private String createWxOutTradeNo(String stationId){
		return "WX"+stationId+System.currentTimeMillis()+getRandom(5);
	}

	@Override
	public String wxWebPay(String msg, String hqId, String stationId,String wxMerchantId) throws Exception {
		log.info("stationId="+stationId+" using wxWebPay,params="+msg);
		PayCriteria criteria = gson.fromJson(msg, new TypeToken<PayCriteria>(){}.getType());
		criteria.setHqId(hqId);
		criteria.setStationId(stationId);
		criteria.setOutTradeNo(createWxOutTradeNo(stationId));
		criteria.setSubMchId(wxMerchantId);
		WxPayReqData reqData = (WxPayReqData) wxDataFactory.getDataByType(criteria,PAY_KEY,PayMethodType.CHARGE.getId());
		int backup = backupPayData(criteria);
		if(backup < 1){//撤销支付
			return RESPONSE_WX_PAY_FAIL;
		}
		String response = httpManager.sendWxPost(WX_UNIFIED_ORDER, reqData);
		WxPayResData resData = handlerWxXmlToResData(response);
		if(resData.getReturn_code().equals(WX_RETURN_CODE_SUCCESS)){//通讯成功
			if(resData.getResult_code().equals(WX_RETURN_CODE_SUCCESS)){//下单成功
				PayOrderTO order = new PayOrderTO(0);
				order.setPayOrderId(criteria.getOutTradeNo());
				order.setPrePayId(resData.getPrepay_id());
				order.setCodeUrl(resData.getCode_url());
				order.sign(PAY_KEY);
				return returnResult(CODE_OK, STRING_OK, order);
			}else{
				return returnResult(CODE_WX_ERROR, resData.getErr_code_des(), null);
			}
		}else{
			return RESPONSE_WX_NO_RESPONSE;
		}
	}

	@Override
	public WxReturnData notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String result = getXmlData(request);
		log.info(result);
		WxPayResData resData = handlerWxXmlToResData(result);
		if(checkIsCharge(resData.getOut_trade_no())){
			log.info("outTradeNo="+resData.getOut_trade_no()+" has charged");
			WxReturnData reqData = new WxReturnData();
			reqData.setReturn_code(WX_RETURN_CODE_SUCCESS);
			reqData.setReturn_msg("OK");
			return reqData;
		}
		if(resData.getResult_code().equals(WX_RETURN_CODE_SUCCESS)){
			if(checkWxSignture(resData)){
				//由数据中心充值
				String hqStr = resData.getOut_trade_no();
				String hqId = hqStr.substring(2, 7);
				String stationId = hqStr.substring(2, 11);
				String resultRes = httpManager.memberCharge(hqId, resData.getOpenid(), resData.getTotal_fee(),
						resData.getOut_trade_no());
				log.info("stationId="+stationId+" charge in center , result="+resultRes);
				AccountChargeHistoryTO to = gson.fromJson(resultRes, new TypeToken<AccountChargeHistoryTO>(){}.getType());
				if(to.getCode() != 0){
					log.error("center charge happen error  response="+resultRes+",  params="+result);
					return null;
				}
				//更新数据
				PayCriteria criteria = getWxPayCriteria(resData);
				criteria.setHqId(hqId);
				criteria.setStationId(stationId);
				criteria.setFuellingOrderId(String.valueOf(to.getId()));
				//回调成功后，持久化
				updatePayData(criteria,resData,result);
			}
		}else{
			log.error("wx pay notify,return="+result);
		}
		WxReturnData reqData = new WxReturnData();
		reqData.setReturn_code(WX_RETURN_CODE_SUCCESS);
		reqData.setReturn_msg("OK");
		return reqData;
	}
	
	@Override
	public String closeWebPay(String payOrderId, String hqId, String stationId,String wxMerchantId) throws Exception {
		PayCriteria criteria = new PayCriteria();
		criteria.setHqId(hqId);
		criteria.setStationId(stationId);
		criteria.setOutTradeNo(payOrderId);
		criteria.setSubMchId(wxMerchantId);
		WxPayReqData reqData = (WxPayReqData) wxDataFactory.getDataByType(criteria,PAY_KEY,PayMethodType.WEBCLOSE.getId());
		String response = httpManager.sendWxPost(WX_WX_CLOSE_WEB, reqData);
		WxPayResData resData = handlerWxXmlToResData(response);
		if(resData.getReturn_code().equals(WX_RETURN_CODE_SUCCESS)){//通讯成功
			if(resData.getResult_code().equals(WX_RETURN_CODE_SUCCESS)){//取消成功
				criteria.setStatus(-1);
				wxPayMapper.updateWxPayOrder(criteria);
				log.info("stationId="+stationId+" closeWebPay, params="+payOrderId);
				return returnResult(CODE_OK, STRING_OK, null);
			}
		}else{
			log.warn("closeWebPay fail, result="+response + ",params="+payOrderId);
		}
		return RESPONSE_WX_PAY_FAIL;
	}
	
	/**
	 * 备份支付前的数据以便查询操作
	 * @param criteria
	 * @return
	 */
	private int backupPayData(PayCriteria criteria){
		criteria.setStatus(0);
		criteria.setBody("微信消费");
		return wxPayMapper.insertWxPayOrder(criteria);
	}
	
	/**
	 * 更新数据
	 * @param criteria
	 * @return
	 */
	private int updatePayData(PayCriteria criteria, WxPayResData resData,String response){
		criteria.setResult(response);
		criteria.setStatus(1);
		return wxPayMapper.updateWxPayOrder(criteria);
	}
	
	/**
	 * 该回调数据是否已充值过
	 * @param outTradeNo
	 * @return
	 */
	private boolean checkIsCharge(String outTradeNo){
		if(wxPayMapper.checkWxPayOrderIsCharge(outTradeNo) > 0){
			return true;
		}
		return false;
	}
	
	/**
	 * 
	 * @param request
	 * @return
	 */
	private String getXmlData(HttpServletRequest request) {
		ServletInputStream sis = null;
		String xmlData = null;
		try {
			// 取HTTP请求流
			sis = request.getInputStream();
			// 取HTTP请求流长度
			int size = request.getContentLength();
			// 用于缓存每次读取的数据
			byte[] buffer = new byte[size];
			// 用于存放结果的数组
			byte[] xmldataByte = new byte[size];
			int count = 0;
			int rbyte = 0;
			// 循环读取
			while (count < size) {
				// 每次实际读取长度存于rbyte中
				rbyte = sis.read(buffer);
				for (int i = 0; i < rbyte; i++) {
					xmldataByte[count + i] = buffer[i];
				}
				count += rbyte;
			}
			xmlData = new String(xmldataByte, "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return xmlData;
	}
	
	/**
	 * 验签
	 * @param resData
	 * @return
	 */
	public boolean checkWxSignture(WxPayResData resData){
		String sign = resData.getSign();
		resData.setSign(null);
		String checkSign = TencentSignature.getSign(resData.toMap(),PAY_KEY);
		if(checkSign.equals(sign)){
			return true;
		}
		return false;
	}
	
//	public static void main(String[] args) {
//		String s = "<xml>"
//				+ "<appid><![CDATA[wx5d7213f7ae8e6fdf]]></appid>"
//				+ "<bank_type><![CDATA[CFT]]></bank_type>"
//				+ "<cash_fee><![CDATA[1]]></cash_fee>"
//				+ "<fee_type><![CDATA[CNY]]></fee_type>"
//				+ "<is_subscribe><![CDATA[Y]]></is_subscribe>"
//				+ "<mch_id><![CDATA[1302785201]]></mch_id>"
//				+ "<nonce_str><![CDATA[92dttjjzgp09ejjni5it28ufyssyrudt]]></nonce_str>"
//				+ "<openid><![CDATA[ovNRGs45WudglvjfjUth3W57tJOU]]></openid>"
//				+ "<out_trade_no><![CDATA[WX16548146287414078510915]]></out_trade_no>"
//				+ "<result_code><![CDATA[SUCCESS]]></result_code>"
//				+ "<return_code><![CDATA[SUCCESS]]></return_code>"
//				+ "<sign><![CDATA[2B9D9FB98EFA20A8299E929D3BD22EB5]]></sign>"
//				+ "<sub_mch_id><![CDATA[1324420101]]></sub_mch_id>"
//				+ "<time_end><![CDATA[20160510175550]]></time_end>"
//				+ "<total_fee>1</total_fee>"
//				+ "<trade_type><![CDATA[JSAPI]]></trade_type>"
//				+ "<transaction_id><![CDATA[4003672001201605105706264429]]></transaction_id></xml>";
//		WxServiceImpl se = new WxServiceImpl();
//		WxPayResData resData = se.handlerWxXmlToResData(s);
//		se.checkWxSignture(resData);
//	}
	
	private PayCriteria getWxPayCriteria(WxPayResData resData){
		PayCriteria criteria = new PayCriteria();
		criteria.setSubMchId(resData.getSub_mch_id());
		criteria.setOpenId(resData.getOpenid());
		criteria.setSubOpenId(resData.getSub_openid());
		criteria.setTotalFee(resData.getTotal_fee());
		criteria.setTransactionId(resData.getTransaction_id());
		criteria.setOutTradeNo(resData.getOut_trade_no());
		criteria.setPayTime(resData.getTime_end());
		return criteria;
	}

}
